[アップデート] AWS SAM CLI のTerraform統合機能が任意のplanの結果の読み込みに対応しTerraform Cloudを活用できるようになりました
初めに
先日リリースされたAWS SAM CLIのv1.96.0
でAWS SAM CLIのTerraform統合の利用において任意のplan結果を読み込ませてローカルでテストできるようになりました。
執筆の順番が前後してしまいましたがGAとなったv1.97.0
ではなくv1.96.0
からの対応です。
過去に書いた記事で述べたことがありますが、SAMでのTerraformの利用についてはSAM側でネイティブにTerraform相当の処理をしているのではなく内部的に実際にterraform
コマンドを実行してその出力を元にCloudFormationテンプレートに変換を行うものとなっております。
以前までこの一連の処理でterraform plan
実行されそれを読み込んでビルドする形になっていましたが、今回はそれを自前でどうにかできるようになったアップデートです(sam build
自体は対応しておらず直接sam local
系のコマンドに指定する形にはなっている)。
Terraform普段使わないため任意のplan結果を読み込ませて嬉しいケースというのがあまり明確に出てこなかったのですが、例外処理で出力されるメッセージやAWSのドキュメント上でTerraform Cloudの利用について言及されており目的としてはこちらの利用のためとなりそうです。
※31行目あたり
Terraform Cloudを活用してのローカルでの実行試してみます。
準備
Terraformを利用したプロジェクトは以前検証したものがあるのでそちらを使っています。
$ tree sam-app-terraform/ . ├── hello_world ## lambdaのアプリコード(terraformの構成とは関係ない) │ ├── __init__.py │ └── app.py └── terraform ## ここ配下がterraform関連のファイル ├── environments │ └── dev │ ├── main.tf ## これがメインのファイル │ └── samconfig.toml └── modules └── hello_world └── main.tf
今回会員登録から行っていますが、メールアドレス等の情報を入れるだけなので省略します。
登録後(ログイン後)にローカル環境でterraform login
を実行すると実行の確認がくるのでyesを入力し、その後トークンの発行画面がブラウザで開かれるので有効期限等を入力しトークンを発行、それをプロンプトに貼り付けます。
% terraform login Terraform will request an API token for app.terraform.io using your browser. If login is successful, Terraform will store the token in plain text in the following file for use by subsequent commands: /Users/xxxxxx/.terraform.d/credentials.tfrc.json Do you want to proceed? Only 'yes' will be accepted to confirm. Enter a value: yes --------------------------------------------------------------------------------- Terraform must now open a web browser to the tokens page for app.terraform.io. If a browser does not open this automatically, open the following URL to proceed: https://app.terraform.io/app/settings/tokens?source=terraform-login --------------------------------------------------------------------------------- Generate a token using your browser, and copy-paste it into this prompt. Terraform will store the token in plain text in the following file for use by subsequent commands: /Users/xxxxxx/.terraform.d/credentials.tfrc.json # ここでAPIキー発行画面がブラウザで開くので対応して発行したAPIキーを入力 Token for app.terraform.io: Enter a value: Retrieved token for user xxxxxx --------------------------------------------------------------------------------- - ----- - --------- -- --------- - ----- --------- ------ ------- ------- --------- ---------- ---- ---------- ---------- -- ---------- ---------- Welcome to Terraform Cloud! - ---------- ------- --- ----- --- Documentation: terraform.io/docs/cloud -------- - ---------- ---------- --------- ----- - New to TFC? Follow these steps to instantly apply an example configuration: $ git clone https://github.com/hashicorp/tfc-getting-started.git $ cd tfc-getting-started $ scripts/setup.sh
実行時に読み込まれるtfファイルにハイライト部分の内容があれば良いようです。今回はmain.tf
1ファイルにまとめてます。
1 ワークスペース = 1 Stateの考えになるとのことなので、複数の環境を作る場合はそれごとに別のワークスペースを指定することとなりそうです(佐藤雅樹さんにご教示いただきました。ありがとうございます)。
provider "aws" { region = "ap-northeast-1" } terraform { cloud { organization = "xxxxxxxxxx" workspaces { name = "example-workspace" } } } module "hello_world" { source = "../../modules/hello_world" }
ワークスペースの環境変数としてTerraformの実行に利用するユーザのアクセスキーを設定します。現在ではIAMロールの指定が可能なオプションもあるようですが今回は利用していません。
Terraform Cloudを利用した実行には各種コマンド自体をCloud側で実行するRemoteモードと、ローカルで実行してStateをCloud側で管理するLocalモードがありますが今回はリモート側で実行させたplan結果を利用したいのでRemoteで実行します。現時点ではデフォルトがRemoteなので特に変更する必要はありません。
これで準備完了で設定した環境でコマンドを実行するとTerraform Cloud上で処理が行われるようです。
plan結果の生成
ここでのイメージとしては現状SAM側で内部で実行されているような処理を手動で実行する形になります。
$ terraform init Initializing modules... - hello_world in ../../modules/hello_world Initializing Terraform Cloud... Initializing provider plugins... - Finding latest version of hashicorp/aws... - Finding latest version of hashicorp/null... - Installing hashicorp/aws v5.15.0... - Installed hashicorp/aws v5.15.0 (signed by HashiCorp) - Installing hashicorp/null v3.2.1... - Installed hashicorp/null v3.2.1 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform Cloud has been successfully initialized! You may now begin working with Terraform Cloud. Try running "terraform plan" to see any changes that are required for your infrastructure. If you ever set or change modules or Terraform Settings, run "terraform init" again to reinitialize your working directory.
terraform plan
を実行します。
% terraform plan Running plan in Terraform Cloud. Output will stream here. Pressing Ctrl-C will stop streaming the logs, but will not stop the plan running remotely. Preparing the remote plan... The remote workspace is configured to work with configuration at environments/dev/ relative to the target repository. Terraform will upload the contents of the following directory, excluding files or directories as defined by a .terraformignore file at /Users/xxxx/git/sam-app-terraform/terraform/.terraformignore (if it is present), in order to capture the filesystem context the remote workspace expects: /Users/xxxx/git/sam-app-terraform/terraform To view this run in a browser, visit: https://app.terraform.io/app/xxxxxx/example-workspace/runs/run-xxxxx Waiting for the plan to start... Terraform v1.3.2 on linux_amd64 Initializing plugins and modules... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.hello_world.aws_api_gateway_integration.hello_world will be created + resource "aws_api_gateway_integration" "hello_world" { + cache_namespace = (known after apply) + connection_type = "INTERNET" + http_method = "GET" + id = (known after apply) + passthrough_behavior = (known after apply) + resource_id = (known after apply) + rest_api_id = (known after apply) + timeout_milliseconds = 29000 + type = "AWS_PROXY" + uri = (known after apply) } ##長いので省略 Plan: 7 to add, 0 to change, 0 to destroy.
結果の取得と実行
変更されるリソースは画面上に表示されますがplan実行結果のファイルはTerraform Cloud側が保持しているのでAPIで取得します。
TOKEN
には先ほど発行したAPIトークンをRUN_ID
には先ほどの実行に単一のIDのようなものが割り当てられているのでそれを設定します(RUN_ID
は先ほどのplan実行の17行目やTerraform Cloudの画面より取得する)。
$ export TOKEN=xxxxx $ export RUN_ID=run-xxxx $ curl -O -L \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/vnd.api+json" \ https://app.terraform.io/api/v2/runs/${RUN_ID}/plan/json-output
APIの出力はterraform plan -out xxx
の結果バイナリ相当ものではなくそれをterraform show -json xxx
で実行した相当のもの(jsonファイル)となるようです。
この結果を--terraform-plan-file
オプションに指定してsam local
系のコマンドを動かすとその結果を元に処理してくれます。
## 現状sam build単品では対応していない模様 % sam build --hook-name terraform --terraform-plan-file json-output Usage: sam build [OPTIONS] [RESOURCE_LOGICAL_ID] Try 'sam build -h' for help. Error: No such option: --terraform-plan-file Did you mean --template-file? ## 実行前に一時ファイル系を削除したが特に問題なく実行できる $ sam local invoke --hook-name terraform --terraform-plan-file json-output Running Prepare Hook to prepare the current application Executing prepare hook of hook "terraform" Using provided plan file: json-output Generating metadata file Unresolvable attributes discovered in project, run terraform apply to resolve them. Finished generating metadata file. Storing in /Users/xxxx/git/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/template.json Prepare hook completed and metadata file generated at: /Users/xxxx/git/sam-app-terraform/terraform/environments/dev/.aws-sam-iacs/iacs_metadata/template.json Invoking app.lambda_handler (python3.11) Local image is up-to-date Using local image: public.ecr.aws/lambda/python:3.11-rapid-arm64. Mounting /Users/xxxx/git/sam-app-terraform/hello_world as /var/task:ro,delegated, inside runtime container START RequestId: c90765a7-47a3-4905-b801-2aa307db73d9 Version: $LATEST END RequestId: c90765a7-47a3-4905-b801-2aa307db73d9 REPORT RequestId: c90765a7-47a3-4905-b801-2aa307db73d9 Init Duration: 0.29 ms Duration: 82.82 ms Billed Duration: 83 ms Memory Size: 128 MB Max Memory Used: 128 MB {"statusCode": 200, "body": "{\"message\": \"hello word\"}"}%
なおこの場合.aws-sam/build
は生成されないようです。
total 64 drwxr-xr-x 8 xxxx staff 256 9 7 19:03 . drwxr-xr-x 3 xxxx staff 96 9 5 21:50 .. drwxr-xr-x 3 xxxx staff 96 9 7 19:03 .aws-sam-iacs -rw-r--r-- 1 xxxx staff 2422 9 7 14:58 .terraform.lock.hcl -rw-r--r-- 1 xxxx staff 13786 9 7 18:54 json-output -rw-r--r-- 1 xxxx staff 63 9 7 18:08 main.tf -rw-r--r-- 1 xxxx staff 714 9 7 19:12 samconfig.toml ## CFnに変換された結果やビルドに必要なものは生成されてる $ tree .aws-sam-iacs ... └── iacs_metadata ├── Makefile ├── copy_terraform_built_artifacts.py ├── template.json ├── z_samcli_backend_override └── zip.py
なお今回Terraform統合利用時のこの辺りの自分の理解が怪しいのでアプリ側がビルドなしで通るものを採用していますが、ビルドが必要なものとなると挙動がイメージできていないところがあります。この点については後日検証と記事執筆を予定しています。
sam buildを呼ぶと
Terraform Cloudを使用する設定をした状態でsam build
を実行するとエラーとなります。
% sam build Experimental features are enabled for this session. Visit the docs page to learn more about the AWS Beta terms https://aws.amazon.com/service-terms/. Running Prepare Hook to prepare the current application Executing prepare hook of hook "terraform" Initializing Terraform application ......... Creating terraform plan and getting JSON output ..... Error: Terraform Cloud does not currently support generating local plan files that AWS SAM CLI uses to parse the Terraform project. To use AWS SAM CLI with Terraform Cloud applications, provide a plan file using the --terraform-plan-file flag.
これについては以下で言及されていますがTerraform Cloudを利用する場合planの直接の結果(バイナリの方)には機密情報が含まれるため取得できないためのようです。
sam build
で内部的に実行されるterraform plan -out output
を直接実行してみるとサポートされていない旨が表示されエラーとなります。
% terraform plan -out output ╷ │ Error: Saving a generated plan is currently not supported │ │ Terraform Cloud does not support saving the generated execution plan locally at this time. ╵
SAM側ではおそらくこのエラーを引っ掛けて先ほどのsam build
のようなメッセージを出しているものと思われます。
終わりに
さて今回はSAMのローカルテストで任意のplanファイルが利用可能となったことと、それにより可能となった作業としてTerraform Cloudを併用した実行をご紹介させていただきました。
最初見た時はTerraform Cloudがどういうものか知らなかったので、実行までいくつもコマンドが必要でめんどくさい!1コマンドでできるようにし欲しいと思っていました。
ただ実際に触ってみるとそもそもTerraform Cloudは結果を複数人で共有するようなものであることがわかり実際のユースケースを考えてみると、利用者的にはおそらくCIツールや管理者側でterraform plan
が実行された状態からスタートするので作業的にはcurlでのplanの結果の取得と実際の実行のみでまとめるほどのものでもないなと気づきました。
それに気づくと結構面白そうな機能でGithubと連携したapplyやplanの実行が以下の記事で紹介されていますが、デプロイ前の最終確認としてそのplanを取得し動作確認ということや別の何かと連携してテストをしたり、デプロイ後のバグ発生時にAPIで環境を取得してローカルでも再現できるかの確認等色々なことができそうです。
興味のある方はぜひ一度触ってみてはいかがでしょうか。